package com.dlc.shop.service.impl;


import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dlc.shop.allinpay.service.AllinpayCompanyService;
import com.dlc.shop.bean.dto.FinanceDetailsDto;
import com.dlc.shop.bean.dto.OrderRefundDto;
import com.dlc.shop.bean.dto.RevenueOverviewDto;
import com.dlc.shop.bean.dto.RevenueOverviewDto.TrendData;
import com.dlc.shop.bean.enums.FinanceBizType;
import com.dlc.shop.bean.model.OrderItem;
import com.dlc.shop.bean.param.FinanceDetailsParam;
import com.dlc.shop.bean.param.RevenueOverviewParam;
import com.dlc.shop.common.allinpay.constant.PaySysType;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.i18n.I18nMessage;
import com.dlc.shop.common.util.*;
import com.dlc.shop.dao.RevenueOverviewMapper;
import com.dlc.shop.service.OrderRefundService;
import com.dlc.shop.service.RevenueOverviewService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 财务管理—营收概况
 *
 * @author SJL
 * @date 2020-08-17
 */
@Service
@AllArgsConstructor
public class RevenueOverviewServiceImpl extends ServiceImpl<RevenueOverviewMapper, OrderItem> implements RevenueOverviewService {


    private final RevenueOverviewMapper revenueOverviewMapper;
    private final OrderRefundService orderRefundService;
    private final AllinpayCompanyService allinpayCompanyService;

    @Override
    public RevenueOverviewDto getData(RevenueOverviewParam param) throws ParseException {
        Integer paySysType = allinpayCompanyService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        Date startDate = new Date(param.getStart());
        Date endDate = DateUtil.endOfDay(new Date(param.getEnd()));
        param.setStartTime(startDate);
        param.setEndTime(endDate);

        // 获取时间段内的实际收入、收入金额、支出金额
        RevenueOverviewDto nowPeriod = revenueOverviewMapper.getData(param.getStationId(), param.getStartTime(), param.getEndTime(), paySysType);
        nowPeriod.setActualIncome(Arith.sub(nowPeriod.getIncomeAmount(), nowPeriod.getPayoutAmount()));

        // 趋势图数据
        List<DateParam> everyDays = DateUtils.findEveryDays(param.getStartTime(), param.getEndTime());
        List<TrendData> trendDataList = new ArrayList<>();
        for (DateParam day : everyDays) {
            RevenueOverviewDto data = revenueOverviewMapper.getData(param.getStationId(), day.getStartTime(), day.getEndTime(), paySysType);
            TrendData trendData = new TrendData();

            trendData.setCurrentDate(DateUtil.format(day.getStartTime(), "yyyy-MM-dd"));
            trendData.setIncomeAmount(data.getIncomeAmount());
            trendData.setPayoutAmount(data.getPayoutAmount());
            trendData.setActualIncome(Arith.sub(data.getIncomeAmount(), data.getPayoutAmount()));

            trendDataList.add(trendData);
        }
        nowPeriod.setTrendDataList(trendDataList);

        // 修改时间
        param.setTime(param.getStartTime(), param.getEndTime());

        // 获取前一时间段内的实际收入、收入金额、支出金额
        RevenueOverviewDto oldPeriod = revenueOverviewMapper.getData(param.getStationId(), param.getStartTime(), param.getEndTime(), paySysType);
        oldPeriod.setActualIncome(Arith.sub(oldPeriod.getIncomeAmount(), oldPeriod.getPayoutAmount()));

        // 实际收入-环比 (本期-上期)/上期*100%
        double actualIncomeRatio = Arith.sub(nowPeriod.getActualIncome(), oldPeriod.getActualIncome());
        actualIncomeRatio = Arith.div(actualIncomeRatio, oldPeriod.getActualIncome(), 2);
        nowPeriod.setActualIncomeRatio(actualIncomeRatio);
        // 收入金额-环比
        double incomeAmountRatio = Arith.sub(nowPeriod.getIncomeAmount(), oldPeriod.getIncomeAmount());
        incomeAmountRatio = Arith.div(incomeAmountRatio, oldPeriod.getIncomeAmount(), 2);
        nowPeriod.setIncomeAmountRatio(incomeAmountRatio);
        // 支出金额-环比
        double payoutAmountRatio = Arith.sub(nowPeriod.getPayoutAmount(), oldPeriod.getPayoutAmount());
        payoutAmountRatio = Arith.div(payoutAmountRatio, oldPeriod.getPayoutAmount(), 2);
        nowPeriod.setPayoutAmountRatio(payoutAmountRatio);

        return nowPeriod;
    }

    @Override
    public IPage<FinanceDetailsDto> getPageDetail(PageParam<FinanceDetailsDto> page, FinanceDetailsParam param) {
        Integer paySysType = allinpayCompanyService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        param.setLang(I18nMessage.getDbLang());
        IPage<FinanceDetailsDto> pageDetail = revenueOverviewMapper.getPageDetail(page, param, paySysType);
        List<FinanceDetailsDto> records = pageDetail.getRecords();
        // 处理下退款的商品名称
        handleProdName(records);
        return pageDetail;
    }

    private void handleProdName(List<FinanceDetailsDto> financeDetails) {
        List<String> refundSnList = financeDetails.stream().filter(detail -> Objects.equals(detail.getBizType(), FinanceBizType.REFUND.value()))
                .map(FinanceDetailsDto::getOrderNumber).collect(Collectors.toList());
        List<OrderRefundDto> orderRefundList = orderRefundService.listRefundInfoByRefundSnList(refundSnList);
        Map<String, OrderRefundDto> orderRefundMap = orderRefundList.stream().collect(Collectors.toMap(OrderRefundDto::getRefundSn, orderRefund -> orderRefund));
        for (FinanceDetailsDto financeDetail : financeDetails) {
            if (!Objects.equals(financeDetail.getBizType(), FinanceBizType.REFUND.value())) {
                continue;
            }
            OrderRefundDto orderRefundDto = orderRefundMap.get(financeDetail.getOrderNumber());
            List<String> prodNameList = orderRefundDto.getOrderItems().stream().map(OrderItem::getProdName).toList();
            String prodName = prodNameList.stream().map(String::valueOf).collect(Collectors.joining(","));
            financeDetail.setProdName(prodName);
        }
    }

    @Override
    public void excelFianceDetail(FinanceDetailsParam param, HttpServletResponse response) {
        if (Objects.isNull(param.getStartTime()) || Objects.isNull(param.getEndTime())) {
            // 请选择导出报表的交易时间
            throw new YamiShopBindException("yami.finance.form.excel");
        }
        Integer paySysType = allinpayCompanyService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        param.setEndTime(DateUtil.endOfDay(param.getEndTime()));
        param.setLang(I18nMessage.getDbLang());
        List<FinanceDetailsDto> financeDetailForm = revenueOverviewMapper.getFinanceDetailForm(param, paySysType);
        handleProdName(financeDetailForm);
        excel(financeDetailForm, response);
    }


    private void excel(List<FinanceDetailsDto> param, HttpServletResponse response) {
        ExcelWriter writer = ExcelUtil.getBigWriter();
        String[] headerCn = new String[]{"交易时间","订单编号","下单时间","交易摘要","发生渠道","资金变更申请人","支付平台交易单号","业务类型","支付方式","收支金额"};
        String[] headerEn = new String[]{"Transaction Time", "Order Number", "Order Placement Time", "Transaction Summary", "Occurrence Channel", "Fund Change Applicant", "Payment Platform Transaction Number", "Business Type", "Payment Method", "Amount of Receipts and Disbursements"};
        List<String> headerList = Arrays.asList(Objects.equals(I18nMessage.getDbLang(), 0) ? headerCn : headerEn);
        Sheet sheet = writer.getSheet();
        int length = headerCn.length;
        for (int i = 0; i < length; i++) {
            sheet.setColumnWidth(i, 20 * 256);
        }
        writer.merge(headerList.size() - 1, I18nMessage.getMessage("yami.finance.reportTitle"));
        writer.writeRow(headerList);
        int row = 1;
        for (FinanceDetailsDto dto : param) {
            int firstRow = ++row;
            int col = -1;
            // 交易时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getTransDate());
            // 订单编号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getOrderNumber());
            // 下单时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getPlaceTime());
            // 交易摘要
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getProdName());
            // 发生渠道
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getShopName());
            // 资金变更申请人
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getUserName());
            // 支付平台交易单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getBizPayNo());
            // 业务类型
            FinanceBizType financeBizType = FinanceBizType.instance(dto.getBizType());
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, Objects.isNull(financeBizType) ? "" : I18nMessage.getMessage(financeBizType.getI18nTag()));
            // 支付方式
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, Objects.equals(dto.getPayType(), -1) ? "" : I18nMessage.getMessage("yami.payType.pay" + dto.getPayType()));
            // 收支金额
            PoiExcelUtil.mergeIfNeed(writer, firstRow, firstRow, ++col, col, dto.getTransAmount());
        }
        PoiExcelUtil.writeExcel(response, writer);
    }
}
